﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace AbstractBugTracker
{
    public sealed class ExceptionReportMapper : IExceptionReportMapper
    {
        private readonly IDictionary<string, string> m_MethodTypeNameMapper;
        private readonly IDictionary<string, string> m_ExceptionTypeNameMapper;

        public ExceptionReportMapper(IDictionary<string, string> methodTypeNameMapper, IDictionary<string, string> exceptionTypeNameMapper)
        {
            m_MethodTypeNameMapper = methodTypeNameMapper;
            m_ExceptionTypeNameMapper = exceptionTypeNameMapper;
        }

        public string MapMethodTypeName(string input)
        {
            return IterativelyUpdate(input, i => m_MethodTypeNameMapper.Aggregate(i, (j, pair) => j.Replace(pair.Key, pair.Value)));
        }

        public string MapExceptionTypeName(string input)
        {
            string nextI;
            return IterativelyUpdate(input, i => m_ExceptionTypeNameMapper.TryGetValue(i, out nextI) ? nextI : i);
        }

        private static string IterativelyUpdate(string input, Func<string, string> func)
        {
            HashSet<string> seenValues = new HashSet<string>();
            string i = input;
            while (true)
            {
                bool added = seenValues.Add(i);
                if (!added)
                {
                    string message = string.Format("Infinite loop detected and abandoned: {0} mapped to {1}",
                        input, string.Join(",", seenValues.ToArray()));
                    throw new InvalidOperationException(message);
                }

                string nextI = func(i);
                if (nextI == i)
                {
                    return i;
                }

                i = nextI;
            }
        }
    }
}
